﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace Region.Reports
{
    class ProcessorAttribute : Attribute
    {
        public string TemplateClass { get; set; }
    }
    class BaseCellAttribute : Attribute
    {
        public string Table { get; set; }
        public string Col { get; set; }
        public string Row { get; set; }
        public string[] ColList { get; set; }
        public string[] RowList { get; set; }
    }
    class BaseHeaderAttribute : Attribute
    {
        public string Table { get; set; }
        public string Header { get; set; }        
        public string[] HeaderList { get; set; }
    }
    class ColHeaderAttribute : BaseHeaderAttribute
    {
        public string Col { get{return Header;} set {Header = value;} }
        public string[] ColList { get{return HeaderList;} set {HeaderList = value;} }
    }
    class RowHeaderAttribute : BaseHeaderAttribute
    {
        public string Row { get{return Header;} set {Header = value;} }
        public string[] RowList { get{return HeaderList;} set {HeaderList = value;} }
    }
    class CheckerCellAttribute : BaseCellAttribute
    {        
    }
    class ResultCellAttribute : BaseCellAttribute
    {
    }
    class HeaderAttribute : BaseCellAttribute
    {
    }
    class ReaderListAttribute : Attribute
    {        
    }
    public interface IHeaderInfo
    {
        String Title{get;}
        String Name{get;}
        String ParentName{get;}
    }
    class HeaderInfo : IHeaderInfo
    {
        public String Title { get; set; }
        public String Name { get; set; }
        public String ParentName { get; set; }
    }
    interface IResultDataInfo
    {
        List<RegionObject> getList<RegionObject>();
    }
    class ReportProcessor
    {
        static bool compareDelegate(Type DelegateType, System.Reflection.MethodInfo met)
        {
            System.Reflection.MethodInfo dt = DelegateType.GetMethod("Invoke");
            if (met.ReturnType != dt.ReturnType)
                return false;
            System.Reflection.ParameterInfo[] met_param = met.GetParameters();
            System.Reflection.ParameterInfo[] dt_param = dt.GetParameters();
            if (met_param.Length != dt_param.Length)
                return false;
            for (Int32 i = 0; i < met_param.Length; i++)
                if (met_param[i].ParameterType != dt_param[i].ParameterType)
                    return false;
            return true;
        }

        class ResultDataInfo: IResultDataInfo
        {
            Dictionary<Type, Object> m_Info = new Dictionary<Type, Object>();
            public void Clear()
            {
                m_Info.Clear();
            }
            public void setList<RegionObject>(List<RegionObject> list)
            {
                m_Info[typeof(RegionObject)] = list;
            }
            public void setList(Type RegionObjectType, Object list)
            {
                m_Info[RegionObjectType] = list;
            }
            public List<RegionObject> getList<RegionObject>()
            {
                if (m_Info.ContainsKey(typeof(RegionObject)))
                    return (List<RegionObject>)m_Info[typeof(RegionObject)];
                return new List<RegionObject>();
            }
        }
        public class ReportReader<Report, RegionObject, CellResult, CellAttribute>
            where CellAttribute : BaseCellAttribute
        {
            protected delegate CellResult dCell(RegionObject p, Report r);
            protected delegate CellResult dCellCol(RegionObject p, Report r, String Col);
            protected delegate CellResult dCellRowCol(RegionObject p, Report r, String Row, String Col);
            public delegate void dReadAllList(List<RegionObject> all, Report r);
            protected Dictionary<String, Boolean> Tables = new Dictionary<String, Boolean>();
            protected Dictionary<String, Dictionary<String, Boolean>> Rows = new Dictionary<String, Dictionary<String, Boolean>>();
            protected Dictionary<String, Dictionary<String, Boolean>> Cols = new Dictionary<String, Dictionary<String, Boolean>>();
            protected Dictionary<String, dCell> Cells = new Dictionary<String, dCell>();
            protected List<dReadAllList> ReadAllList = new List<dReadAllList>();
            protected void Clear()
            {
                Tables.Clear();
                Rows.Clear();
                Cols.Clear();
                Cells.Clear();
                ReadAllList.Clear();
            }
            protected void addCheckerCell(String Table, String Row, String Col, dCell checker)
            {
                String CellName = "";
                if (Table != null)
                    CellName += "_t" + Table;
                if (Row != null)
                    CellName += "_r" + Row;
                if (Col != null)
                    CellName += "_c" + Col;
                if (Table != null)
                    Tables[Table] = true;
                if (Table != null && Row != null)
                {
                    if (!Rows.ContainsKey(Table))
                        Rows[Table] = new Dictionary<String, Boolean>();
                    Rows[Table][Row] = true;
                }
                if (Table != null && Col != null)
                {
                    if (!Cols.ContainsKey(Table))
                        Cols[Table] = new Dictionary<String, Boolean>();
                    Cols[Table][Col] = true;
                }
                Cells[CellName] = checker;
            }

            /// <summary> Загрузка обработчиков отчёта </summary>               
            protected void LoadReportInfo(String reportClass, String NameSpace = "Region.Reports")
            {
                Clear();
                Type rp = Type.GetType(NameSpace + "." + reportClass);
                if (rp == null)
                    return;
                foreach (var met in rp.GetMethods())
                {
                    var attL = (ReaderListAttribute)Attribute.GetCustomAttribute(met, typeof(ReaderListAttribute));
                    if (attL != null && compareDelegate(typeof(dReadAllList),met))                        
                    {
                        ReadAllList.Add((dReadAllList)Delegate.CreateDelegate(typeof(dReadAllList), met));
                    }
                    var att = (CellAttribute)Attribute.GetCustomAttribute(met, typeof(CellAttribute));
                    if (att != null)
                    {
                        if (att.ColList != null && att.RowList != null)
                        {
                            if (att != null && compareDelegate(typeof(dCellRowCol), met))
                            {
                                var checker = (dCellRowCol)Delegate.CreateDelegate(typeof(dCellRowCol), met);
                                for (Int32 i = 0; i < att.ColList.Length; i++)
                                    for (Int32 j = 0; j < att.RowList.Length; j++)
                                    {
                                        String Col = att.ColList[i];
                                        String Row = att.ColList[i];
                                        addCheckerCell(att.Table, Row, Col, (obj, report) =>
                                        {
                                            return checker(obj, report, Row, Col);
                                        });
                                    }
                            }
                        }
                        else if (att.ColList != null)
                        {
                            if (att != null && compareDelegate(typeof(dCellCol), met))
                            {
                                var checker = (dCellCol)Delegate.CreateDelegate(typeof(dCellCol), met);
                                for (Int32 i = 0; i < att.ColList.Length; i++)
                                {
                                    String Col = att.ColList[i];
                                    addCheckerCell(att.Table, att.Row, Col, (obj, report) =>
                                    {
                                        return checker(obj, report, Col);
                                    });
                                }
                            }
                        }
                        else if (att.RowList != null)
                        {
                            if (att != null && compareDelegate(typeof(dCellCol), met))
                            {
                                var checker = (dCellCol)Delegate.CreateDelegate(typeof(dCellCol), met);
                                for (Int32 i = 0; i < att.RowList.Length; i++)
                                {
                                    String Row = att.RowList[i];
                                    addCheckerCell(att.Table, Row, att.Col, (obj, report) =>
                                    {
                                        return checker(obj, report, Row);
                                    });
                                }
                            }
                        }
                        else
                            if (att != null && compareDelegate(typeof(dCell), met))
                                addCheckerCell(att.Table, att.Row, att.Col, (dCell)Delegate.CreateDelegate(typeof(dCell), met));
                    }
                }
            }
            protected delegate void iterCell(String Table, String Row, String Col, dCell[] CellCheckerList, Int32 NumCellCheckerList);
            /// <summary> Переберает все ячейки </summary>
            /// <param name="dIterCell">void iterCell(String Table,String Row, String Col, dCell[] CellCheckerList,Int32 NumCellCheckerList)</param>
            /// <param name="cellList">Делегаты ячеек по умалчанию</param>
            /// <param name="NumCellCheckerList">количество делегатов</param>
            protected void ForeachCells(iterCell dIterCell, dCell[] cellList = null, Int32 NumCellList = 0)
            {
                Int32 maxCellList = 4;
                if (cellList != null)
                    maxCellList += cellList.Length;
                var CellList = new dCell[maxCellList];
                if (cellList != null)
                {
                    if (NumCellList == 0)
                        NumCellList = cellList.Length;
                    for (Int32 i = 0; i < NumCellList; i++)
                        CellList[i] = cellList[i];
                }
                else
                    NumCellList = 0;
                foreach (String Table in Tables.Keys)
                    if (Rows.ContainsKey(Table) && Cols.ContainsKey(Table))
                    {
                        Int32 NumCellCheckerListTable = NumCellList;
                        String OnlyTable = "_t" + Table;
                        if (Cells.ContainsKey(OnlyTable))
                            CellList[NumCellCheckerListTable++] = Cells[OnlyTable];
                        foreach (String Row in Rows[Table].Keys)
                        {
                            Int32 NumCellCheckerListRow = NumCellCheckerListTable;
                            String OnlyRow = OnlyTable + "_r" + Row;
                            if (Cells.ContainsKey(OnlyRow))
                                CellList[NumCellCheckerListRow++] = Cells[OnlyRow];
                            foreach (String Col in Cols[Table].Keys)
                            {
                                Int32 NumCellCheckerListCol = NumCellCheckerListRow;
                                String OnlyCol = OnlyTable + "_c" + Col;
                                if (Cells.ContainsKey(OnlyCol))
                                    CellList[NumCellCheckerListCol++] = Cells[OnlyCol];

                                String Cell = OnlyTable + "_r" + Row + "_c" + Col;
                                if (Cells.ContainsKey(Cell))
                                    CellList[NumCellCheckerListCol++] = Cells[Cell];
                                dIterCell(Table, Row, Col, CellList, NumCellCheckerListCol);
                            }
                        }
                    }
            }
        }
        public class ReportResultInfo<Report, CellResult> : ReportReader<Report, IResultDataInfo, CellResult, ResultCellAttribute>
            where CellResult: class
        {            
            Report m_report;
            Dictionary<Type, Object> m_Info = new Dictionary<Type, Object>();
            public void setInfo<RegionObject>(ReportProccessInfo<Report,RegionObject> list)
            {
                m_Info[typeof(RegionObject)] = list;
            }
            List<RegionObject> getInfo<RegionObject>()
            {
                if (m_Info.ContainsKey(typeof(RegionObject)))
                    return (List<RegionObject>)m_Info[typeof(RegionObject)];
                return new List<RegionObject>();
            }
            public delegate void dSetValue(String Table,String Row,String Col, CellResult Result);
            public delegate void dSetResultInfo(String Table, String Row, String Col, IResultDataInfo Info);
            public ReportResultInfo(Report report)
            {                
                m_report = report;
            }
            void setList<RegionObject>(ResultDataInfo info, ReportProccessInfo<Report,RegionObject> Info, String Table, String Row, String Col)
            {
                if (Info.ReportLists.ContainsKey(Table) && Info.ReportLists[Table].ContainsKey(Row) && Info.ReportLists[Table][Row].ContainsKey(Col))
                    info.setList<RegionObject>(Info.ReportLists[Table][Row][Col]);
                else
                    info.setList<RegionObject>(new List<RegionObject>());
            }

            void ProcessCells(dSetValue SetValue, dSetResultInfo SetResultInfo = null)
            {
                var resultInfo = new ResultDataInfo();
                ForeachCells((Table, Row, Col, CellResultList, NumCellCheckerList) =>
                {
                    resultInfo.Clear();
                    foreach (var info in m_Info)
                    {
                        var rd = (IReportData)info.Value;
                        resultInfo.setList(info.Key, rd.getReportData(Table, Row, Col));
                    }
                    CellResult result = null ;
                    for (Int32 i = NumCellCheckerList - 1; result == null && i >= 0; i--)
                    {
                        result = CellResultList[i](resultInfo, m_report);
                    }
                    SetValue(Table, Row, Col, result);
                    if (SetResultInfo != null)
                        SetResultInfo(Table, Row, Col, resultInfo);
                });
            }
            public void Process(String reportClass, dSetValue SetValue, dSetResultInfo SetResultInfo = null )
            {
                // Загрузка информации  о классе обработчике            
                LoadReportInfo(reportClass);
                if (Cells.Count == 0)
                    return;
                ProcessCells(SetValue, SetResultInfo);
            }
        }
        public class ReportHeader<Report, HeaderType, HeaderAtt>
            where HeaderAtt: BaseHeaderAttribute
            where HeaderType: class
        {            
            protected delegate HeaderType dHeader(String Header, Report r);            
            protected Dictionary<String, Boolean> Tables = new Dictionary<String, Boolean>();
            protected Dictionary<String, dHeader> Headers = new Dictionary<String, dHeader>();
            void Clear()
            {
                Headers.Clear();
            }
            public HeaderType getHeader(String Table,String HeaderName)
            {
                HeaderType h = null;
                String HName1 = "_t" + Table + "_h" + HeaderName;
                if (Headers.ContainsKey(HName1))
                    h = Headers[HName1](HeaderName, m_report);
                String HName2 = "_t" + Table;
                if (h==null && Headers.ContainsKey(HName2))
                    h = Headers[HName2](HeaderName, m_report);
                return h;
            }
            Report m_report;
            protected void addHeader(String Table,String HeaderName, dHeader header)
            {
                String HName = "";
                if (Table != null)
                    HName += "_t" + Table;
                if (HeaderName != null)
                    HName += "_h" + HeaderName;
                if (Table != null)
                    Tables[Table] = true;
                Headers[HName] = header;
            }
            public ReportHeader(Report report)
            {
                m_report = report;
            }
            protected void LoadHeaderInfo(String reportClass, String NameSpace = "Region.Reports")
            {
                Clear();
                Type rp = Type.GetType(NameSpace + "." + reportClass);
                if (rp == null)
                    return;
                foreach (var met in rp.GetMethods())
                {
                    var att = (HeaderAtt)Attribute.GetCustomAttribute(met, typeof(HeaderAtt));
                    if (att != null && compareDelegate(typeof(dHeader), met))
                    {
                        if (att.HeaderList != null)
                        {
                            foreach (String HeaderName in att.HeaderList)
                                addHeader(att.Table, HeaderName, (dHeader)Delegate.CreateDelegate(typeof(dHeader), met));
                        }
                        else
                        {
                            addHeader(att.Table, att.Header, (dHeader)Delegate.CreateDelegate(typeof(dHeader), met));
                        }
                    }
                }
            }
            public void Process(String reportClass)
            {
                LoadHeaderInfo(reportClass);
            }
        }
        /**/
        public interface IReportData
        {
            Object getReportData(String Table,String Row,String Col);
        }
        public class ReportProccessInfo<Report, RegionObject> : ReportReader<Report, RegionObject, Boolean, CheckerCellAttribute>, IReportData
        {
            Report m_report;
            List<RegionObject> ReportList = new List<RegionObject>();
            public delegate void dReadAllDefaultList(List<RegionObject> all);
            //Table-Row-Col-List
            public Dictionary<String, Dictionary<String, Dictionary<String, List<RegionObject>>>> ReportLists = new Dictionary<String, Dictionary<String, Dictionary<String, List<RegionObject>>>>();
            public Object getReportData(String Table, String Row, String Col)
            {
                if (ReportLists.ContainsKey(Table) && ReportLists[Table].ContainsKey(Row) && ReportLists[Table][Row].ContainsKey(Col))
                    return ReportLists[Table][Row][Col];
                return null;
            }
            public ReportProccessInfo(Report report)
            {
                m_report = report;
            }
            /// <summary> Загрузка списка для всего отчёта </summary>
            void LoadAllList(dReadAllDefaultList loadDefaultList = null)
            {
                var AllList = new List<RegionObject>();                
                if (ReadAllList.Count() > 0)
                {
                    foreach (var readAll in ReadAllList)
                        readAll(AllList,m_report);
                }
                else if (loadDefaultList != null)
                    loadDefaultList(AllList);
                var AllChecker = Cells[""];
                if (AllChecker != null)
                {
                    foreach (RegionObject obj in AllList)
                        if (AllChecker(obj, m_report))
                            ReportList.Add(obj);
                }
                else
                    ReportList = AllList;// Нет основного проверщика будет обрабатывать все объекты        
            }
            void ProcessCells()
            {
                ForeachCells((Table, Row, Col, CellCheckerList, NumCellCheckerList) =>
                {
                    var CellList = new List<RegionObject>();
                    foreach (var obj in ReportList)
                    {
                        Boolean AddObject = true;
                        for (Int32 i = 0; AddObject && i < NumCellCheckerList; i++)
                        {
                            if (!CellCheckerList[i](obj, m_report))
                                AddObject = false;
                        }
                        if (AddObject)
                            CellList.Add(obj);
                    }
                    if (!ReportLists.ContainsKey(Table))
                        ReportLists[Table] = new Dictionary<String, Dictionary<String, List<RegionObject>>>();
                    if (!ReportLists[Table].ContainsKey(Row))
                        ReportLists[Table][Row] = new Dictionary<String, List<RegionObject>>();
                    ReportLists[Table][Row][Col] = CellList;
                });
            }
            public void Process(String reportClass, dReadAllDefaultList loadDefaultList)
            {
                // Загрузка информации  о классе обработчике            
                LoadReportInfo(reportClass);
                if (Cells.Count == 0)
                    return;
                LoadAllList(loadDefaultList);
                ProcessCells();
            }
        }
    }
    
}
